from __future__ import print_function
from __future__ import unicode_literals

import wx

from kharlia import core, wxinter

from kharlia.engine.base import FSizeI
from kharlia.engine.client import CClientViewport, EKey, EMouseButton

__all__ = [
    'CViewport',
    'CViewportFrame'
    ]

# Translations from wxPython keys to local engine (CEGUI based) keycodes
# This should only include special non-unicode keys
GKeyTranslations = {
    0x7E: EKey.GRAVE, # wx uses ASCII tilde code for this key
    wx.WXK_LEFT: EKey.ARROW_LEFT,
    wx.WXK_RIGHT: EKey.ARROW_RIGHT,
    wx.WXK_DOWN: EKey.ARROW_DOWN,
    wx.WXK_UP: EKey.ARROW_UP,
    wx.WXK_RETURN: EKey.RETURN,
    wx.WXK_BACK: EKey.BACKSPACE,
    #wx.WXK_SPACE: EKey.SPACE,
    wx.WXK_DELETE: EKey.DELETE,
    }
    
# Add ASCII mappings
#for i in xrange(ord('A'), ord('Z') + 1):
#    GKeyTranslations[i] = getattr(engine, 'KEY_' + chr(i))
    
#print('KEY TRANSLATIONS:')
#for k, v in GKeyTranslations.iteritems():
#    print(k, '=', int(v))

#def TranslateKey(code):
#    #if code >= 65 or code <= 90:
#    #    
#    return GKeyTranslations.get(code, None)

class CViewport(wx.Window, CClientViewport):
    def __init__(self, *args, **kwds):
        wx.Window.__init__(self, *args, **kwds)
        self.WindowStyle |= wx.WANTS_CHARS
        self.Size = None
        self.Resizing = False
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftMouseDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftMouseUp)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightMouseDown)
        self.Bind(wx.EVT_RIGHT_UP, self.OnRightMouseUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        self.Bind(wx.EVT_CHAR, self.OnChar)
        self.XHandle = self.GetHandle()
        self.Engine = None
        self.Client = None
    
    def __del__(self):
        self._ClearWindowHandle()
        super(CViewport, self).__del__()

    # CClientViewport interface
    
    def GetWindowHandle(self):
        return self.XHandle
    
    def CheckWindowHandle(self):
        return True
    
    def OnClientInited(self, client):
        self.Client = client
        self.Engine = client.Engine
        
    def OnClientExiting(self):
        self.Client = None
        self.Engine = None
    
    # end CClientViewport interface
    
    def _ClearWindowHandle(self):
        if (self.Client is not None and
            self.Client.WindowHandle == self.XHandle):
            self.Engine.ExitClient()

    def Destroy(self):
        self._ClearWindowHandle()
        super(CViewport, self).Destroy()
        
    def OnFrameActivate(self, event):
        if self.Client:
            self.Client.OnViewportActivate(self, event.GetActive())
        event.Skip()

    def OnKeyDown(self, event):
        cl = self.Client
        if cl is not None:
            special, keycode, unikey = wxinter.TranslateKeyEvent(event)
            ecode = GKeyTranslations.get(keycode, None)
            if ecode is not None:
                cl.InputKeyDown(ecode)
            # We want both key down and char events to be injected, so
            # we skip always to allow OnChar to occur.
            event.Skip()

    def OnChar(self, event):
        cl = self.Client
        if cl is not None:
            special, keycode, unikey = wxinter.TranslateKeyEvent(event)
            # the delete key has a printable unicode character '.', exclude it
            if unikey and keycode != wx.WXK_DELETE:
                cl.InputChar(unikey)

    def OnKeyUp(self, event):
        cl = self.Client
        if cl is not None:
            ecode = GKeyTranslations.get(event.GetKeyCode(), None)
            if ecode is not None:
                cl.InputKeyUp(ecode)
        
    def HandleResize(self):
        if self.Client is None:
            return
        mouseState = wx.GetMouseState().LeftDown()
        if self.Resizing and mouseState is False:
            self.Resizing = False
            self.Client.ViewportSize = FSizeI(self.Size.width, self.Size.height)
        else: 
            if self.Resizing and mouseState:
                wx.CallLater(10, self.HandleResize)

    def OnSize(self, event):
        if self.Client is None:
            return
        size = self.Size = self.GetClientSize()
        mouseState = wx.GetMouseState().LeftDown()
        if mouseState is not True:
            self.Client.ViewportSize = FSizeI(self.Size.width, self.Size.height)
        else:
            if self.Resizing is not True:
                self.Resizing = True
                wx.CallLater(10, self.HandleResize)
        #event.Skip()

    def OnLeftMouseDown(self, evt):
        if self.Client is None:
            return
        self.Client.InputMouseButtonDown(EMouseButton.LEFT)
        self.CaptureMouse()

    def OnLeftMouseUp(self, evt):
        if self.Client is None:
            return
        self.Client.InputMouseButtonUp(EMouseButton.LEFT)
        if self.HasCapture():
            self.ReleaseMouse()
            
    def OnRightMouseDown(self, evt):
        if self.Client is None:
            return
        self.Client.InputMouseButtonDown(EMouseButton.RIGHT)
        self.CaptureMouse()
        
    def OnRightMouseUp(self, evt):
        if self.Client is None:
            return
        self.Client.InputMouseButtonUp(EMouseButton.RIGHT)
        if self.HasCapture():
            self.ReleaseMouse()

    def OnMouseMotion(self, evt):
        if self.Client is None:
            return
        self.Client.InputMousePosition(evt.GetX(), evt.GetY())

class CViewportFrame(wx.Frame):
    '''
    Basic frame for games. Holds a single viewport for engine rendering.
    
    Arguments:
    title -- the window title
    vpsize -- viewport size, must be sequence
    closecallback -- called when close is requested, should return True
                     to destroy the viewport and frame, False to ignore.
    '''
    def __init__(self, title, vpsize, closecallback):
        assert callable(closecallback)        
        wx.Frame.__init__(self, parent=None, title=title)
        self.CloseCallback = closecallback
        self.SetClientSizeWH(vpsize[0], vpsize[1])
        self.Resizing = False
        self.Viewport = CViewport(self)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
    
    def OnClose(self, event):
        if self.CloseCallback():
            self.Hide()
            self.Viewport.Destroy()
            self.Viewport = None
            self.Destroy()

    def OnActivate(self, event):
        self.Viewport.OnFrameActivate(event)